/*
 * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "app_sr.h"
#include "esp_afe_sr_models.h"
#include "esp_mn_models.h"
#include "freertos/FreeRTOS.h"
#include "freertos/queue.h"
#include "freertos/task.h"

// #include "app_play_music.h"
#include "app_sr_handler.h"
#include "model_path.h"
#include "esp_mn_speech_commands.h"
#include "esp_process_sdkconfig.h"
// #include "bsp_board_extra.h"
#include "i2s.h"

#define I2S_CHANNEL_NUM     (2)

static const char *TAG = "app_sr";

static model_iface_data_t       *model_data     = NULL;
static const esp_mn_iface_t     *multinet       = NULL;
static const esp_afe_sr_iface_t *afe_handle     = NULL;
static QueueHandle_t            g_result_que    = NULL;
static srmodel_list_t           *models         = NULL;

const char *cmd_phoneme[12] = {
    "da kai kong qi jing hua qi",
    "guan bi kong qi jing hua qi",
    "da kai tai deng",
    "guan bi tai deng",
    "tai deng tiao liang",
    "tai deng tiao an",
    "da kai deng dai",
    "guan bi deng dai",
    "bo fang yin yue",
    "ting zhi bo fang",
    "da kai shi jian",
    "da kai ri li"
};

// 音频采集任务
static void audio_feed_task(void *pvParam)
{
    size_t bytes_read = 0;
    esp_afe_sr_data_t *afe_data = (esp_afe_sr_data_t *) pvParam;
    int audio_chunksize = afe_handle->get_feed_chunksize(afe_data);// 获取单帧数据大小
    ESP_LOGI(TAG, "audio_chunksize=%d, feed_channel=%d", audio_chunksize, 2);

    /* Allocate audio buffer and check for result */
    // 参考单帧大小malloc一个音频buffer，如果有psram则malloc在psram上
    int16_t *audio_buffer = heap_caps_malloc(audio_chunksize * sizeof(int16_t) * 2, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    if (NULL == audio_buffer) {
        esp_system_abort("No mem for audio buffer");
    }

    while (true) {
        /* Read audio data from I2S bus */
        // 持续从IIS读取音频数据
        bytes_read = i2s_rx_read((uint8_t *)audio_buffer, audio_chunksize * 2 * sizeof(int16_t));
        //printf("IIS读取了%d\n", bytes_read);
        if (bytes_read == 0 ) {
            ESP_LOGE(TAG, "======== bsp_extra_i2s_read failed ==========");
        }

        /* Channel Adjust */
        // 声学前端需要三声道，左声道+右声道+回采，IIS只收集左右声道，所以加入全0的第三声道
        // for (int  i = audio_chunksize - 1; i >= 0; i--) {
        //     audio_buffer[i * 3 + 2] = 0;
        //     audio_buffer[i * 3 + 1] = audio_buffer[i * 2 + 1];
        //     audio_buffer[i * 3 + 0] = audio_buffer[i * 2 + 0];
        // }

        /* Feed samples of an audio stream to the AFE_SR */
        // 如果读取成功则传给声学前端进行检测
        afe_handle->feed(afe_data, audio_buffer);
        vTaskDelay(pdMS_TO_TICKS(1)); 
    }

    /* Clean up if audio feed ends */
    afe_handle->destroy(afe_data);

    /* Task never returns */
    vTaskDelete(NULL);
}

// 音频检测任务
static void audio_detect_task(void *pvParam)
{
    bool detect_flag = false;
    esp_afe_sr_data_t *afe_data = (esp_afe_sr_data_t *) pvParam;

    /* Check if audio data has same chunksize with multinet */
    // 确认音频的采样大小和命令词模型的采样大小是否一致，默认每32ms喂一次
    int afe_chunksize = afe_handle->get_fetch_chunksize(afe_data);
    int mu_chunksize = multinet->get_samp_chunksize(model_data);
    // assert(mu_chunksize == afe_chunksize);
    ESP_LOGI(TAG, "------------detect start------------\n");

    while (true) {
        // 从声学前端获取采集并处理后的音频数据
        afe_fetch_result_t *res = afe_handle->fetch(afe_data);
        // 新增日志
        // ESP_LOGI(TAG, "fetch result: %p, ret_value: %d", res, res ? res->ret_value : -1);
        if (!res || res->ret_value == ESP_FAIL) {
            ESP_LOGE(TAG, "fetch error!");
            continue;
        }

        // if (res->vad_state == 1) {
        //     ESP_LOGI(TAG, "VAD detected voice - 可能正在说话\n");
        // }

        // 打印唤醒状态（例如：0=未检测，1=检测到）
        //ESP_LOGI(TAG, "wakeup_state: %d, VAD state: %d", res->wakeup_state, res->vad_state);

        // 如果检测到唤醒词，就把检测结果通过消息队列发送给处理任务
        if (res->wakeup_state == WAKENET_DETECTED) {
            ESP_LOGI(TAG, LOG_BOLD(LOG_COLOR_GREEN) "Wakeword detected");
            sr_result_t result = {
                .wakenet_mode = WAKENET_DETECTED,
                .state = ESP_MN_STATE_DETECTING,
                .command_id = 0,
            };
            xQueueSend(g_result_que, &result, 10);
        } else if (res->wakeup_state == WAKENET_CHANNEL_VERIFIED) {
            // 唤醒后，延时3帧开启命令词识别，关闭唤醒词识别
            // ESP_LOGI(TAG, LOG_BOLD(LOG_COLOR_GREEN) "Channel verified");
            // detect_flag = true;
            // afe_handle->disable_wakenet(afe_data);
        }

        vTaskDelay(pdMS_TO_TICKS(1)); 
    }

    /* Clean up if audio feed ends */
    afe_handle->destroy(afe_data);

    /* Task never returns */
    vTaskDelete(NULL);
}

esp_err_t app_sr_start(void)
{
    // 创建识别结果队列
    g_result_que = xQueueCreate(1, sizeof(sr_result_t));
    ESP_RETURN_ON_FALSE(NULL != g_result_que, ESP_ERR_NO_MEM, TAG, "Failed create result queue");

    // 获取所有模型列表
    models = esp_srmodel_init("model");

    // 新增：检查models是否初始化成功
    ESP_RETURN_ON_FALSE(models != NULL && models->num > 0, ESP_FAIL, TAG, "Failed to load models from 'model' path");

    // 新增：打印所有加载的模型名称，确认是否有唤醒词模型（通常以"wn"开头）
    for (int i = 0; i < models->num; i++) {
        ESP_LOGI(TAG, "Loaded model: %s", models->model_name[i]);
    }

    // 用默认配置来初始化一个声学前端句柄以及AFE的配置结构体
    // 已弃用
    // afe_handle = &ESP_AFE_SR_HANDLE;
    // afe_config_t afe_config = AFE_CONFIG_DEFAULT();

    afe_config_t *afe_config = afe_config_init("MM", models, AFE_TYPE_SR, AFE_MODE_HIGH_PERF);
    afe_config_print(afe_config); // print all configurations
    afe_handle = esp_afe_handle_from_config(afe_config);

    // 从模型队列中过滤出之前配置好的唤醒词模型名字
    afe_config->wakenet_model_name = esp_srmodel_filter(models, ESP_WN_PREFIX, NULL);
    afe_config->aec_init = true;    // 为了节省资源，关掉回声消除

    // 新增：VAD（语音活动检测）：优化参数过滤短时噪声
    afe_config->vad_init = true;
    afe_config->vad_mode = VAD_MODE_2; // 中等灵敏度
    afe_config->vad_model_name = NULL; // 使用默认WebRTC VAD
    afe_config->vad_min_speech_ms = 200; // 最小语音持续200ms（过滤敲击声等）
    afe_config->vad_min_noise_ms = 800; // 最小噪声持续800ms
    afe_config->vad_delay_ms = 128;
    afe_config->vad_mute_playback = false;
    afe_config->vad_enable_channel_trigger = false;

    // 新增：AGC（自动增益控制）：平衡音量，避免噪声放大
    afe_config->agc_init = true;
    afe_config->agc_mode = AFE_AGC_MODE_WAKENET; // 适配唤醒场景
    afe_config->agc_compression_gain_db = 10; // 适度增益（10dB）
    afe_config->agc_target_level_dbfs = 8; // 目标电平-8dBFS
    
    afe_config->afe_ringbuf_size = 100;  // 从50帧增至100帧（根据内存情况可调整至200）

    // 使用这个AFE的配置结构体创建AFE数据对象afe_data，可以用来保存AFE相关的所有状态信息
    esp_afe_sr_data_t *afe_data = afe_handle->create_from_config(afe_config);
    ESP_LOGI(TAG, "load wakenet:%s", afe_config->wakenet_model_name); // 打印唤醒词的名字确认一下

    /*********************************** 唤醒词模型加载完成 ***********************************/
    
    for (int i = 0; i < models->num; i++) {
        ESP_LOGI(TAG, "Current Model:%s", models->model_name[i]);
    }

    char *mn_name = esp_srmodel_filter(models, ESP_MN_CHINESE, NULL);
    if (NULL == mn_name) {
        ESP_LOGE(TAG, "No multinet model found");
        return ESP_FAIL;
    }

    multinet = esp_mn_handle_from_name(mn_name);
    model_data = multinet->create(mn_name, 5760);
    ESP_LOGI(TAG, "load multinet:%s", mn_name);

    /*********************************** 命令词模型加载完成 ***********************************/

    esp_mn_commands_clear();

    for (int i = 0; i < sizeof(cmd_phoneme) / sizeof(cmd_phoneme[0]); i++) {
        esp_mn_commands_add(i, (char *)cmd_phoneme[i]);
    }

    esp_mn_commands_update();
    esp_mn_commands_print();
    multinet->print_active_speech_commands(model_data);

    /*********************************** 注册语音指令完成 ***********************************/

    // 采集音频数据，core1，高优先级
    BaseType_t ret_val = xTaskCreatePinnedToCore(audio_feed_task, "Feed Task", 4 * 1024, afe_data, 5, NULL, 1);
    ESP_RETURN_ON_FALSE(pdPASS == ret_val, ESP_FAIL, TAG,  "Failed create audio feed task");

    // 检测音频数据，core0，高优先级
    ret_val = xTaskCreatePinnedToCore(audio_detect_task, "Detect Task", 6 * 1024, afe_data, 5, NULL, 0);
    ESP_RETURN_ON_FALSE(pdPASS == ret_val, ESP_FAIL, TAG,  "Failed create audio detect task");

    // 处理检测结果，不需要高优先级 
    ret_val = xTaskCreatePinnedToCore(sr_handler_task, "SR Handler Task", 4 * 1024, g_result_que, 1, NULL, 1);
    ESP_RETURN_ON_FALSE(pdPASS == ret_val, ESP_FAIL, TAG,  "Failed create audio handler task");

    return ESP_OK;
}

esp_err_t app_sr_reset_command_list(char *command_list)
{
    char *err_id = heap_caps_malloc(1024, MALLOC_CAP_SPIRAM);
    ESP_RETURN_ON_FALSE(NULL != err_id, ESP_ERR_NO_MEM, TAG,  "memory is not enough");
    free(err_id);
    return ESP_OK;
}